#!/usr/bin/perl
use strict;
use FindBin;
use JSON;
use Getopt::Long;
use File::Glob qw(bsd_glob);
use Cwd;

use ServerAdapter;
use TagentClient;
use DeployUtils;
use DeployLock;

sub usage {
    my $pname = $FindBin::Script;

    print("Usage: $pname [--envpath EnvPath] [--version VERSION] [--verbose 0|1]\n");
    print("              [--pdir approot|project|release|appdist|dbscript|mirror]\n");
    print("              [--np 0|1] [--followlinks 0|1]\n");
    print("              [-x ExcludeSubDirs] [--pull 0|1]\n");
    print("              <src1> <src2> <dest>\n");
    print("\n");
    print("       copy files to remote server\n");
    print("       --envpath:           Env path in the data directory, example:ATM/ATMP/PRD/ATMP-1\n");
    print("       --version:           version number of sub system\n");
    print("       --pull:              pull from remote site to local site\n");
    print("       --pdir:              sub directory type of this system\n");
    print("       --np    : not preserve the mode of file\n");
    print("       --cpifexists: copy if local dir or file exists.\n");
    print("       --followlinks:       symbol link as directory\n");
    print("       --x:                 do not symc sub dir or files, example:sub1/sub2,sub3\n");

    exit(1);
}

sub main {
    my ( $isHelp, $isVerbose, $envPath, $version, $node );
    my ( $pdir,   $np,        $cpIfExists );
    my ( $host,   $port,      $user, $pass, $expDir );
    my $isPull            = 0;
    my $isPush            = 1;
    my $noDelete          = 0;
    my $noAttrs           = 0;
    my $addExecForNewFile = 0;
    my $followLinks       = 0;
    my $pname             = $FindBin::Script;

    $isVerbose = 0;
    $pdir      = undef;

    GetOptions(
        'v|verbose=i'   => \$isVerbose,
        'envpath=s'     => \$envPath,
        'version=s'     => \$version,
        'h|help'        => \$isHelp,
        'pdir=s'        => \$pdir,
        'np=i'          => \$np,
        'cpifexists'    => \$cpIfExists,
        'followlinks=i' => \$followLinks,
        'pull=i'        => \$isPull,
        'x|exp=s'       => \$expDir,
        'node=s'        => \$node,
        '<>'            => \&pushItems
    );

    my @dirs;

    sub pushItems {
        my ($item) = @_;
        push( @dirs, $item );
    }

    my $deployUtils = DeployUtils->new();

    my $optionError = 0;

    if ( $isPull == 0 ) {
        $isPush = 1;
    }

    if ( scalar(@dirs) < 2 ) {
        print("ERROR: Must define local path and remote path.\n");
        $optionError = 1;
    }

    for ( my $i = 0 ; $i < scalar(@dirs) ; $i++ ) {
        my $dir = $dirs[$i];
        if ( $dir =~ /\/\.\.\// or $dir =~ /^\.\.\// or $dir =~ /\/\.\.$/ ) {
            print("ERROR: Path can not has parent dir opertor:\"..\".\n");
            $optionError = 1;
        }
        else {
            $dir = $deployUtils->charsetConv( $dirs[$i], 'utf-8' );
            $dirs[$i] = $dir;
        }
    }

    my $deployEnv = $deployUtils->deployInit( $envPath, $version );
    $envPath = $deployEnv->{NAME_PATH};
    $version = $deployEnv->{VERSION};

    if ( not defined($envPath) or $envPath eq '' ) {
        $optionError = 1;
        print("ERROR: EnvPath not defined by option --envpath or Environment:NAME_PATH\n");
    }
    if ( not defined($version) or $version eq '' ) {
        $optionError = 1;
        print("ERROR: Version not defined by option --version or Environment:VERSION\n");
    }

    my $namePath = $deployEnv->{NAME_PATH};
    my $envName  = $deployEnv->{ENV_NAME};

    my $serverAdapter = ServerAdapter->new();

    my $verInfo   = $serverAdapter->getEnvVer( $deployEnv, $version );
    my $verStatus = $verInfo->{status};
    my $buildNo   = $verInfo->{buildNo};
    my $isMirror  = $verInfo->{isMirror};
    if ( $verInfo->{status} ne 'released' ) {
        print("ERROR: $namePath version:$version is not released to $envName.\n");
        return 3;
    }

    if ( $isMirror == 1 ) {
        $pdir = 'mirror';
    }
    else {
        $pdir = 'appdist';
    }

    my $dirInfo = $deployUtils->getDataDirStruct($deployEnv);
    my $verPath = $dirInfo->{$pdir};

    if ( defined($pdir) ) {
        if ( not defined($verPath) ) {
            print("ERROR: $pdir not valid, not in appsync|mirror|appbuild|version.\n");
            $optionError = 1;
        }
    }

    my $nodeInfo = $deployUtils->getNodeInfo($node);
    if ( not $nodeInfo ) {
        $optionError = 1;
        print("ERROR: Execute node json not defined by environment AUTOEXEC_NODE or option --node\n");
    }

    if ( $optionError == 1 ) {
        usage();
    }

    my $direction = 'push';
    if ( $isPull == 1 ) {
        $direction = 'pull';
    }

    my $envName = $deployEnv->{ENV_NAME};
    my $verRoot = $dirInfo->{appRoot};

    my $host  = $nodeInfo->{host};
    my $port  = $nodeInfo->{protocolPort};
    my $user  = $nodeInfo->{username};
    my $pass  = $nodeInfo->{password};
    my $insId = $nodeInfo->{resourceId};

    #my $insName = $nodeInfo->{nodeName};
    my $insUniqName = $nodeInfo->{nodeUniqName};

    if ( defined($pass) and $pass ne '' ) {
        $pass = $deployUtils->decryptPwd($pass);
    }

    $port = 22 if ( not defined($port) );

    my $lock = DeployLock->new($deployEnv);
    my $appLockId;

    if ( $isMirror == 1 ) {
        $appLockId = $lock->lockMirror($DeployLock::WRITE);
    }
    else {
        $appLockId = $lock->lockEnvApp($DeployLock::WRITE);
    }

    END {
        local $?;
        if ( defined($lock) ) {
            if ( $isMirror == 1 ) {
                $lock->unlockMirror($appLockId);
            }
            else {
                $lock->unlockEnvApp($appLockId);
            }
        }
    }

    my $verDiffPath = "$verPath.ins/$insUniqName";

    my $tagent = new TagentClient( $host, $port, $pass );

    my $hasError = 0;

    my ( @srcs, $dest );
    $dest = pop(@dirs);

    if ($isPush) {
        foreach my $dir (@dirs) {
            my $aSrc = "$verPath/$dir";

            my @aSrcExpanded = bsd_glob($aSrc);

            if ( scalar(@aSrcExpanded) == 0 ) {
                if ( defined($cpIfExists) ) {
                    print("WARN: The src $aSrc does not match any file!\n");
                    next;
                }
            }

            foreach my $aDir (@aSrcExpanded) {
                push( @srcs, $aDir );
            }

            my @diffDirs = bsd_glob("$verDiffPath/$dir");
            foreach my $diffDir (@diffDirs) {
                push( @srcs, $diffDir );
            }
        }

        foreach my $src (@srcs) {
            my $status = $tagent->upload( $user, $src, $dest, $isVerbose, 0, $followLinks );
            $hasError = $hasError + $status;
        }
    }
    elsif ($isPull) {
        $dest = "$verPath/$dest";
        foreach my $dir (@dirs) {
            my $status = $tagent->download( $user, $dir, $dest, $isVerbose, $followLinks );
            $hasError = $hasError + $status;
        }
    }

    if ( $hasError == 0 ) {
        print( "FINE: $pname --$direction " . join( ',', @dirs ) . " $dest success.\n" );
    }
    else {
        print( "ERROR: $pname --$direction " . join( ',', @dirs ) . " $dest failed.\n" );
    }

    return $hasError;
}

exit main();

